package com.roamer.audition.web.config;

import com.roamer.audition.web.security.JwtValidConfigurer;
import com.roamer.audition.web.security.LoginConfigurer;
import com.roamer.audition.web.security.handler.*;
import com.roamer.audition.web.security.provider.JwtAuthenticationProvider;
import com.roamer.audition.web.security.service.JwtUserService;
import com.roamer.audition.web.security.service.SystemPlatformUserUserServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;
import java.util.Collections;

/**
 * WebSecurityConfig
 *
 * @author roamer
 * @version v1.0
 * @date 2020/4/20 17:20
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 密码加密器
     *
     * @return {@code PasswordEncoder}密码加密器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public JwtUserService jwtUserService() {
        return new SystemPlatformUserUserServiceImpl(passwordEncoder());
    }

    /**
     * CORS 跨域
     *
     * @return {@code CorsConfigurationSource}
     */
    @Bean
    protected CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Collections.singletonList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
        configuration.setAllowedHeaders(Collections.singletonList("*"));
        configuration.addExposedHeader("Authorization");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    //    /**
    //     * CORS 跨域
    //     *
    //     * @return {@code CorsConfigurationSource}
    //     */
    //    @Bean
    //    protected FilterRegistrationBean<CorsFilter> filterRegistrationBean() {
    //        CorsConfiguration configuration = new CorsConfiguration();
    //        configuration.setAllowedOrigins(Collections.singletonList("*"));
    //        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "HEAD", "OPTION"));
    //        configuration.setAllowedHeaders(Collections.singletonList("*"));
    //        configuration.addExposedHeader("Authorization");
    //        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    //        source.registerCorsConfiguration("/**", configuration);
    //        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
    //        bean.setOrder(0);
    //        return bean;
    //    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
            // swagger
            .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/images/**", "/webjars/**", "/v2/api-docs",
                         "/configuration/ui", "/configuration/security")
            .permitAll()
            // 请求认证
            .antMatchers("/companies/**")
            .hasRole("ADMIN")
            .antMatchers("/auditionRecords/**")
            .hasAnyRole("ADMIN", "USER")
            // 默认其它的请求都需要认证，这里一定要添加
            .anyRequest()
            .authenticated()
            .and()
            // CRSF禁用，因为不使用session
            .csrf()
            .disable()
            // 禁用session
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            // 禁用form登录
            .formLogin()
            .disable()
            // 支持跨域
            .cors()
            .and()
            //                .headers().addHeaderWriter(new StaticHeadersWriter(Arrays.asList(
            //                new Header("Access-control-Allow-Origin","*"),
            //                new Header("Access-Control-Expose-Headers","Authorization"))))
            //                .and()
            // 拦截OPTIONS请求，直接返回header
            //                .addFilterAfter(new OptionsRequestFilter(), CorsFilter.class)
            // 添加登录filter
            .apply(new LoginConfigurer<>())
            .loginSuccessHandler(loginSuccessHandler())
            .and()
            // 添加token的filter
            .apply(new JwtValidConfigurer<>())
            .tokenValidSuccessHandler(jwtRefreshSuccessHandler())
            .permissiveRequestUrls("/login", "/logout")
            .and()
            // 使用默认的logoutFilter
            .logout()
            // 默认就是"/logout"
            .logoutUrl("/logout")
            // logout时清除token
            .addLogoutHandler(logoutClearTokenHandler())
            // logout成功后返回200
            .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())
            .and()
            .exceptionHandling()
            /// 处理未认证的情况
            .authenticationEntryPoint(new GlobalAuthenticationEntryPoint())
            .accessDeniedHandler(new GlobalAccessDeniedHandler());
    }

    /**
     * AuthenticationManager 认证管理器
     *
     * @return {@code AuthenticationManager} 返回默认AuthenticationManager
     *
     * @throws Exception 未知异常
     */
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 登陆成功处理器
     *
     * @return {@code AuthenticationSuccessHandler}
     */
    @Bean("loginSuccessHandler")
    protected AuthenticationSuccessHandler loginSuccessHandler() {
        return new LoginSuccessHandler(jwtUserService());
    }

    /**
     * 登出处理器
     *
     * @return {@code LogoutHandler}
     */
    @Bean("logoutClearTokenHandler")
    protected LogoutHandler logoutClearTokenHandler() {
        return new LogoutClearTokenHandler(jwtUserService());
    }


    /**
     * AuthenticationProvider
     * username login认证
     *
     * @return {@code AuthenticationProvider}
     */
    @Bean("daoAuthenticationProvider")
    protected AuthenticationProvider daoAuthenticationProvider() {
        // 这里会默认使用BCryptPasswordEncoder比对加密后的密码，注意要跟createUser时保持一致
        DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();
        daoProvider.setPasswordEncoder(passwordEncoder());
        daoProvider.setUserDetailsService((UserDetailsService) jwtUserService());
        return daoProvider;
    }

    /**
     * JWT Provider
     *
     * @param auth AuthenticationManagerBuilder
     *
     * @throws Exception 未知异常
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProvider()).authenticationProvider(jwtAuthenticationProvider());
    }

    /**
     * JWT 认证器
     *
     * @return
     */
    @Bean("jwtAuthenticationProvider")
    protected AuthenticationProvider jwtAuthenticationProvider() {
        return new JwtAuthenticationProvider(jwtUserService());
    }

    /**
     * JWT认证成功处理器
     *
     * @return {@code AuthenticationSuccessHandler}
     */
    @Bean("jwtRefreshSuccessHandler")
    protected AuthenticationSuccessHandler jwtRefreshSuccessHandler() {
        return new JwtRefreshSuccessHandler(jwtUserService());
    }


}
